#include "ssd1306.h"

#include "stdlib.h"
#include "math.h"
#include "string.h"

#include "fcntl.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "unistd.h"
#include "sys/ioctl.h"
#include "linux/i2c-dev.h"

#include "unistd.h"

#include "pthread.h"

/*
两个函数都是底层操作函数，不与任何高层数据抽象结构相关
*/
int SSD1306_send_cmd (int hi2c, unsigned char cmd)
{
  unsigned char sending_buffer[2];
  
  sending_buffer[0] = 0x00;
  sending_buffer[1] = cmd;

  return write (hi2c, sending_buffer, 2);
}

int SSD1306_send_data (int hi2c, unsigned char *data, unsigned int data_size)
{
  unsigned char sending_buffer[data_size + 1];
  
  sending_buffer[0] = 0x40;
  
  memcpy (sending_buffer + 1, data, data_size);
  
  return write (hi2c, sending_buffer, data_size + 1);
}

/*required obj->dev_addr*/
int SSD1306_Init (struct SSD1306_display *obj, char *dev_path)
{
  int res;
  
  if (obj == NULL) return -2;
  
  res = obj->fd = open (dev_path, O_WRONLY);
  if (res < 0) return res;
  
  res = ioctl (obj->fd, I2C_SLAVE_FORCE, obj->dev_addr);
  if (res < 0) return res;
  
  obj->frames_head = NULL;
  
  SSD1306_send_cmd(obj->fd,0xAE);//关闭显示

  SSD1306_send_cmd(obj->fd,0xD5);//设置时钟分频因子,震荡频率
  SSD1306_send_cmd(obj->fd,80);  //[3:0],分频因子;[7:4],震荡频率

  SSD1306_send_cmd(obj->fd,0xA8);//设置驱动路数
  SSD1306_send_cmd(obj->fd,0X3F);//默认0X3F(hi2c,1/64)

  SSD1306_send_cmd(obj->fd,0xD3);//设置显示偏移
  SSD1306_send_cmd(obj->fd,0X00);//默认为0

  SSD1306_send_cmd(obj->fd,0x40);//设置显示开始行 [5:0],行数.

  SSD1306_send_cmd(obj->fd,0x8D);//电荷泵设置
  SSD1306_send_cmd(obj->fd,0x14);//bit2，开启/关闭

  SSD1306_send_cmd(obj->fd,0x20);//设置内存地址模式
  SSD1306_send_cmd(obj->fd,0x00);
  
  SSD1306_send_cmd(obj->fd,0xA1);//段重定义设置,bit0:0,0->0;1,0->127;
  SSD1306_send_cmd(obj->fd,0xC0);
  
  SSD1306_send_cmd(obj->fd,0xDA);//设置COM硬件引脚配置
  SSD1306_send_cmd(obj->fd,0x12);//[5:4]配置
  
  SSD1306_send_cmd(obj->fd,0x81);//对比度设置
  SSD1306_send_cmd(obj->fd,0xEF);//1~255;默认0X7F (hi2c,亮度设置,越大越亮)
  
  SSD1306_send_cmd(obj->fd,0xD9);//设置预充电周期
  SSD1306_send_cmd(obj->fd,0xf1);//[3:0],PHASE 1;[7:4],PHASE 2;
  
  SSD1306_send_cmd(obj->fd,0xDB);//设置VCOMH 电压倍率
  SSD1306_send_cmd(obj->fd,0x30);//[6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vcc;
  
  SSD1306_send_cmd(obj->fd,0xA4);//全局显示开启;bit0:1,开启;0,关闭;(白屏/黑屏)
  
  SSD1306_send_cmd(obj->fd,0xA6);//设置显示方式;bit0:1,反相显示;0,正常显示
  
  SSD1306_send_cmd(obj->fd,0xAF);//开启显示
  
  return 0;
}

int SSD1306_UpdateDisplay (struct SSD1306_display *obj, int frame)
{
    int res;
    struct SSD1306_frame_node *p1 = obj->frames_head;
    
    for (int i = 0; i < frame && p1->suc != NULL; ++i) p1 = p1->suc;
    if (obj == NULL) return -2;
    
    SSD1306_send_cmd (obj->fd, 0xb0);
    SSD1306_send_cmd (obj->fd, 0x00);
    SSD1306_send_cmd (obj->fd, 0x10);
    
    res = SSD1306_send_data (obj->fd, p1->context, GRAM_SIZE);
    
    return res;
}

/*
这个函数需要提供显示屏对象和插入帧的前一帧索引号
*/
int SSD1306_CreateFrame (struct SSD1306_display *obj, int frame)
{
  unsigned char *new_frame;
  struct SSD1306_frame_node *p1 = obj->frames_head;
  struct SSD1306_frame_node *p2;
  
  if (obj == NULL) return -2;
  
  if (p1 != NULL)
  {
    for (int i = 0; i < frame && p1->suc != NULL; ++i) p1 = p1->suc;
  }
  
  p2 = malloc (sizeof (struct SSD1306_frame_node));
  if (p2 == NULL) return -1;
  p2->suc = NULL;
  p2->context = NULL;
  new_frame = malloc (GRAM_SIZE);
  if (new_frame == NULL) return -1;
  p2->context = new_frame;
  
  if (p1 == NULL)
  {
    obj->frames_head = p2;
  }
  else
  {
    p2->suc = p1->suc;
    p1->suc = p2;
  }
  
  return 0;
}

/*
这个函数需要提供显示屏对象和需要清除的帧的索引号。
这个函数并不会删除这一帧，而是将这一帧数据完全清零。
*/
int SSD1306_CleanFrame (struct SSD1306_display *obj, int frame)
{
  struct SSD1306_frame_node *p1 = obj->frames_head;
  
  for (int i = 0; i < frame && p1->suc != NULL; ++i) p1 = p1->suc;
  if (obj == NULL) return -2;
  
  for (int i = 0; i < GRAM_SIZE; ++i)
  {
    p1->context[i] = 0x00;
  }
  
  return 0;
}

/*
这个函数需要提供显示屏对象和需要删除的帧的索引号。
这个函数会从链表中删除这一帧，并且释放这一帧占用的空间。
*/
int SSD1306_DestoryFrame (struct SSD1306_display *obj, int frame)
{
  unsigned char **temp;
  
  unsigned char *new_frame;
  struct SSD1306_frame_node *p1 = obj->frames_head;
  struct SSD1306_frame_node *p2;
  
  if (obj == NULL) return -2;
  
  if (frame <= 0)
  {
    obj->frames_head = p1->suc;
    free (p1->context);
    free (p1);
  }
  else
  {
    for (int i = 1; i < frame && p1->suc != NULL; ++i) p1 = p1->suc;
    p2 = p1->suc;
    p1->suc = p2->suc;
    free (p2->context);
    free (p2);
  }
  
  return 0;
}

/*画点函数，实现抽象点到内存空间的转换与写入*/
int SSD1306_WritePoint (struct SSD1306_display *obj, int frame, int x, int y)
{
  int byte_index;
  int bit_index;
  struct SSD1306_frame_node *p1 = obj->frames_head;
  
  for (int i = 0; i < frame && p1->suc != NULL; ++i) p1 = p1->suc;
  if (obj == NULL) return -2;
  
  if (x > 127 || x < 0 || y > 63 || y < 0)
  {
    return -1;
  }
  
  bit_index = y % 8;
  byte_index = (y / 8) * 128 + x;
  
  p1->context[byte_index] |= (1 << bit_index);
  
  return 0;
}

/*画矩形*/
int SSD1306_DrawJX (struct SSD1306_display *obj, int frame, int x1, int y1, int x2, int y2)
{
  for (int i = x1; i <= x2; ++i)
  {
    SSD1306_WritePoint (obj, frame, i, y1);
    SSD1306_WritePoint (obj, frame, i, y2);
  }
  for (int i = y1; i <= y2; ++i)
  {
    SSD1306_WritePoint (obj, frame, x1, i);
    SSD1306_WritePoint (obj, frame, x2, i);
  }
  
  return 0;
}

/*画直线*/
int SSD1306_DrawZX (struct SSD1306_display *obj, int frame, int x1, int y1, int x2, int y2)
{
  double k1 = 1.0 * (y2 - y1) / (x2 - x1);
  int b1 = y1 - k1 * x1;
  
  for (int i = (x1 < x2 ? x1 : x2); i <= (x1 > x2 ? x1 : x2); ++i)
  {
    SSD1306_WritePoint (obj, frame, i, k1 * i + b1);
  }
  
  double k2 = 1.0 * (x2 - x1) / (y2 - y1);
  int b2 = x1 - k2 * y1;
  
  for (int i = (y1 < y2 ? y1 : y2); i <= (y1 > y2 ? y1 : y2); ++i)
  {
    SSD1306_WritePoint (obj, frame, k2 * i + b2, i);
  }
  
  return 0;
}

/*载入一个方阵*/
int SSD1306_DrawFZ (struct SSD1306_display *obj, int frame, int xb, int yb, unsigned char *fz)
{
  struct SSD1306_frame_node *p1 = obj->frames_head;
  
  for (int i = 0; i < frame && p1->suc != NULL; ++i) p1 = p1->suc;
  
  yb *= 2;
  xb = 15 - xb;
  
  for (int i = 0; i < 16; ++i)
  {
    p1->context[(yb - 1) * 128 + 16 * xb + i] = fz[15 - i];
    p1->context[yb * 128 + 16 * xb + i] = fz[31 - i];
  }
  
  return 0;
}

/*画圆形*/
int SSD1306_DrawYX (struct SSD1306_display *obj, int frame, int x, int y, int r)
{
  struct SSD1306_frame_node *p1 = obj->frames_head;
  
  for (int i = 0; i < frame && p1->suc != NULL; ++i) p1 = p1->suc;
  
  for (int i = x - r; i < x + r; ++i)
  {
    int y1 = y + sqrt (pow (r, 2) - pow (i - x, 2));
    int y2 = y - sqrt (pow (r, 2) - pow (i - x, 2));
    
    SSD1306_WritePoint (obj, frame, i, y1);
    SSD1306_WritePoint (obj, frame, i, y2);
  }
  
  for (int i = y - r; i < y + r; ++i)
  {
    int x1 = x + sqrt (pow (r, 2) - pow (i - y, 2));
    int x2 = x - sqrt (pow (r, 2) - pow (i - y, 2));
    
    SSD1306_WritePoint (obj, frame, x1, i);
    SSD1306_WritePoint (obj, frame, x2, i);
  }
  
  return 0;
}
